package me.gavin.base

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.DataSource
import androidx.paging.LivePagedListBuilder
import androidx.paging.PageKeyedDataSource
import androidx.paging.PagedList
import kotlinx.coroutines.*

typealias Block<T> = suspend (CoroutineScope) -> T
typealias Error = suspend (Exception) -> Unit
typealias Cancel = suspend (Exception) -> Unit

abstract class BaseViewModel : ViewModel() {

    val loginStatusInvalid: MutableLiveData<Boolean> = MutableLiveData()

    /**
     * 创建并执行协程
     * @param block 协程中执行
     * @param error 错误时执行
     * @param cancel 取消时只需
     * @param showErrorToast 是否弹出错误吐司
     * @return Job
     */
    protected fun launch(
        block: Block<Unit>,
        error: Error? = null,
        cancel: Cancel? = null,
        showErrorToast: Boolean = true
    ): Job = viewModelScope.launch {
        try {
            block.invoke(this)
        } catch (e: Exception) {
            when (e) {
                is CancellationException -> {
                    cancel?.invoke(e)
                }
                else -> {
                    onError(e, showErrorToast)
                    error?.invoke(e)
                }
            }
        }
    }

    /**
     * 创建并执行协程
     * @param block 协程中执行
     * @return Deferred<T>
     */
    protected fun <T> async(block: Block<T>): Deferred<T> {
        return viewModelScope.async { block.invoke(this) }
    }

    /**
     * 取消协程
     * @param job 协程job
     */
    protected fun cancelJob(job: Job?) {
        if (job != null && job.isActive && !job.isCompleted && !job.isCancelled) {
            job.cancel()
        }
    }

    /**
     * 统一处理错误
     * @param e 异常
     * @param showErrorToast 是否显示错误吐司
     */
    private fun onError(e: Exception, showErrorToast: Boolean) {
//        when (e) {
//            is ApiException -> {
//                when (e.code) {
//                    -1001 -> {
//                        // 登录失效，清除用户信息、清除cookie/token
//                        UserInfoStore.clearUserInfo()
//                        RetrofitClient.clearCookie()
//                        loginStatusInvalid.value = true
//                    }
//                    // 其他api错误
//                    -1 -> if (showErrorToast) App.instance.showToast(e.message)
//                    // 其他错误
//                    else -> if (showErrorToast) App.instance.showToast(e.message)
//                }
//            }
//            // 网络请求失败
//            is ConnectException, is SocketTimeoutException, is UnknownHostException, is HttpException ->
//                if (showErrorToast) App.instance.showToast(R.string.network_request_failed)
//            // 数据解析错误
//            is JsonDataException, is JsonEncodingException ->
//                if (showErrorToast) App.instance.showToast(R.string.api_data_parse_error)
//            // 其他错误
//            else ->
//                if (showErrorToast) App.instance.showToast(e.message ?: return)
//        }
    }
}

abstract class PageViewModel<T> : BaseViewModel() {

    lateinit var dataSource: PageKeyedDataSource<Int, T>

    private var loadMoreRetry: (() -> Unit)? = null

    val refreshState = MutableLiveData(false)

    val pagedList: LiveData<PagedList<T>> by lazy {
        LivePagedListBuilder(
            object : DataSource.Factory<Int, T>() {
                override fun create() = createDataSource.also { dataSource = it }
            },
            PagedList.Config.Builder()
                .setPageSize(10)
                .setInitialLoadSizeHint(30)
                .build()
        ).build()
    }

    val createDataSource
        get() = object : PageKeyedDataSource<Int, T>() {
            override fun loadInitial(
                params: LoadInitialParams<Int>,
                callback: LoadInitialCallback<Int, T>
            ) {
                launch(
                    block = {
                        val list = listData(1)
                        callback.onResult(list, null, 2)
                        refreshState.value = false
                    },
                    error = {
                        refreshState.value = false
                    }
                )
            }

            override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Int, T>) {
                launch(
                    block = {
                        val list = listData(params.key)
                        callback.onResult(list, params.key + 1)
                    },
                    error = {
                        loadMoreRetry = { dataSource.loadAfter(params, callback) }
                    }
                )
            }

            override fun loadBefore(params: LoadParams<Int>, callback: LoadCallback<Int, T>) {}
        }


    abstract suspend fun listData(page: Int): List<T>

    fun refresh() = dataSource.invalidate()

    fun loadMoreRetry() = loadMoreRetry?.invoke()

}